package com.dragon.modules.yuegou.model.rbac;

import com.dragon.modules.yuegou.web.LinkedTree;
import com.dragon.modules.yuegou.web.Tree;
import com.dragon.modules.yuegou.web.TreeIterator;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import javax.annotation.Nullable;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 资源节点，资源节点与资源节点之间组成了一个树形结构
 * 
 * @author liuwu
 *
 */
@ApiModel(description = "数据节点(可以数据权限控制的数据：集团、商圈)")
public class DbacDataNode extends DbacData {

	private static final String[] SELECT_FLAGS = { "  ", "√", "⚪", "×" };

	public static final int UNSELECTED = 0;
	public static final int SELECTED = 1;
	public static final int HALFSELECTED = 2;
	public static final int FORBIDDEN = 3;

	/**
	 * 是否选择：0表示未选择，1表示全选，2表示半选（其子节点被选择了，但是本身没有被选择）<br>
	 * <ol>
	 * <li>未选择表示无权限</li>
	 * <li>全选表示有该资源节点及其所有子节点的权限</li>
	 * <li>半选表示有该节点的子节点的部分权限</li>
	 * <li>禁用表示所属的权限组没有开发对应资源的权限</li>
	 * </ol>
	 */
	@ApiModelProperty("是否选择：0表示未选择，1表示全选，2表示半选（其子节点被选择了，但是本身没有被选择），3表示禁用")
	private int hasSelected =0;

	/**
	 * 本资源节点的子资源列表
	 */
	@Nullable
	@ApiModelProperty("子节点列表")
	private List<DbacDataNode> childs;

	/**
	 * 节点类型
	 */
	@Nullable
	@ApiModelProperty("类型，D代表商圈、G代表集团")
	private String type;

	public DbacDataNode(DbacData data) {
		super(data);
	}

	@JsonCreator
	public DbacDataNode(@JsonProperty("id") String id, @Nullable @JsonProperty("parentId") String parentId,
			@JsonProperty("name") String name) {
		super(id, parentId, name);
	}

	/**
	 * 获取节点类型
	 * @return 节点类型
	 */
	@Nullable
	public String getType() {
		return type;
	}
	/**
	 * 设置节点类型
	 * @param type 节点类型
	 */
	public void setType(String type) {
		this.type = type;
	}

	/**
	 * 获取选择状态：0表示未选择，1表示全选，2表示半选（其子节点被选择了，但是本身没有被选择）
	 * 
	 * @return hasSelected
	 * @see #UNSELECTED
	 * @see #SELECTED
	 * @see #HALFSELECTED
	 */
	public int getHasSelected() {
		return hasSelected;
	}



	/**
	 * 设置选择状态：0表示未选择，1表示全选，2表示半选（其子节点被选择了，但是本身没有被选择）
	 * 
	 * @param hasSelected 选择状态
	 * @see #UNSELECTED
	 * @see #SELECTED
	 * @see #HALFSELECTED
	 */
	public void setHasSelected(int hasSelected) {
		this.hasSelected = hasSelected;
	}

	/**
	 * 判断资源是否被选中
	 * 
	 * @return true表示选中，false表示未被选中
	 */
	@JsonIgnore
	public boolean isSelect() {
		return hasSelected == SELECTED;
	}

	/**
	 * 获取子资源列表
	 * 
	 * @return 子资源列表
	 */
	public List<DbacDataNode> getChilds() {
		return childs;
	}

	/**
	 * 设置子资源列表
	 * 
	 * @param childs 子资源列表
	 */
	public void setChilds(List<DbacDataNode> childs) {
		this.childs = childs;
	}

	/**
	 * 构造数据树
	 * 
	 * @param datas           数据单元集合
	 * @param selectPredicate 选择判断函数
	 * @return 资源树
	 */
	public static Tree<DbacDataNode> buildSelectedTree(Tree<DbacData> datas, Predicate<DbacDataNode> selectPredicate) {
		if (datas.isEmpty()) {
			throw new IllegalArgumentException("数据树不能为空");
		}
		String rootId = datas.root().element().getId();
		List<DbacDataNode> nodes = datas.stream().map(DbacDataNode::new).collect(Collectors.toList());
		Tree<DbacDataNode> tree = LinkedTree.build(nodes, n -> rootId.equals(n.getId()),
				(e, r) -> r.getId().equals(e.getParentId()), (o1, o2) -> o1.getName().compareTo(o2.getName()));
		TreeIterator<DbacDataNode> itr = tree.treeIterator(Tree.IteratorStrategy.BREADTH_FIRST);
		while (itr.hasNext()) {
			handleWhile(itr,selectPredicate);
		}
		return tree;
	}

	private static void handleWhile(TreeIterator<DbacDataNode> itr,Predicate<DbacDataNode> selectPredicate){
		DbacDataNode node = itr.next();
		List<DbacDataNode> childs = itr.childs();
		if (!childs.isEmpty()) {
			node.setChilds(childs);
		}
		if (selectPredicate.test(node)) {
			node.setHasSelected(SELECTED);
			for (DbacDataNode p : itr.path()) {
				if (p.getHasSelected() != SELECTED) {
					p.setHasSelected(HALFSELECTED);
				}
			}
		} else {
			// 继承父节点的选中状态
			List<DbacDataNode> path = itr.path();
			if (path.size() > 2) {
				node.hasSelected = path.get(path.size() - 2).hasSelected;
			}
		}
	}

	@Override
	public String toString() {
		return String.format("[%s]%s", SELECT_FLAGS[hasSelected], name);
	}
}
